#pragma once
#include "../acl_cpp_define.hpp"
#include "../stdlib/string.hpp"
#include "../db/db_handle.hpp"

typedef struct st_mysql MYSQL;

namespace acl {

class mysql_conf;

class ACL_CPP_API db_mysql : public db_handle
{
public:
	/**
	 * 构造函数方式一
	 * @param dbaddr {const char*} 数据库监听地址，可以为 TCP 套接口或在 UNIX
	 *  平台下的域套接口，格式如：127.0.0.1:3306，或 /tmp/mysql.sock
	 * @param dbname {const char*} 数据库名称，非 NULL
	 * @param dbuser {const char*} 连接数据库时的用户名
	 * @param dbpass {const char*} 连接数据库时的用户密码
	 * @param dbflags {unsigned long} 连接 MYSQL 时的标志位
	 * @param auto_commit {bool} 当对数据库进行修改时是否自动提交事务
	 * @param conn_timeout {int} 连接数据库的超时时间（秒）
	 * @param rw_timeout {int} 进行数据库操作时的超时时间（秒）
	 * @param charset {const char*} 连接数据库时的本地字符集（gbk, utf8, ...）
	 */
	db_mysql(const char* dbaddr, const char* dbname,
		const char* dbuser, const char* dbpass,
		unsigned long dbflags = 0, bool auto_commit = true,
		int conn_timeout = 60, int rw_timeout = 60,
		const char* charset = "utf8");

	/**
	 * 构造函数方式二：使用参数配置类对象进行构造
	 * @param conf {const mysql_conf&} mysql 数据库连接配置类对象
	 */
	db_mysql(const mysql_conf& conf);

	/**
	 * mysql操作句柄释放，包括各种参数空间释放
	 */
	~db_mysql(void);

	const char* get_db_mysql_info();

	/**
	 * 直接获得 mysql 的连接句柄，如果返回 NULL 则表示 mysql 还没有打开
	 * 或出错时内部自动关闭了 mysql 连接
	 * @return {MYSQL*}
	 */
	MYSQL* get_conn(void) const
	{
		return conn_;
	}

	/////////////////////////////////////////////////////////////////////

	/**
	 * 获得 mysql 客户端库的版本号
	 * @return {unsigned long}
	 */
	unsigned long mysql_libversion(void) const;

	/**
	 * 获得 mysql 客户端库的信息
	 * @return {const char*}
	 */
	const char* mysql_client_info(void) const;

	/**
	 * 当动态加载 libmysqlclient.so / libmysqlclient.dll 时，可以调用本
	 * 静态函数显式动态加载 mysql 客户端库，如果加载失败，内部会自动产生
	 * 断言，以免运行时出错，也可不调用本函数，使 db_mysql 类对象内部在
	 * 使用时隐式加载 mysql 动态库
	 */
	static void load(void);

	/********************************************************************/
	/*                        以下为基类 db_handle 的虚接口                                                           */
	/********************************************************************/

	/**
	 * @override
	 *
	 * 被db_handle.open()调用 打开数据库连接
	 *
	 * 1) 调用mysql_init初始化MYSQL* conn_连接句柄
	 * 2) 设置网络连接超时时间(秒) conn_timeout_
	 * 3) 设置网络 IO读写 超时时间(秒) rw_timeout_
	 * 4) 设置为自动重连
	 * 5) 调用mysql_real_connect连接数据库。如果连接成功，返回MYSQL*连接句柄。如果连接失败，返回NULL。
	 * 6) 设置连接的字符集
	 * 7) 设置是否auto_commit
	 */
	bool dbopen(const char* charset = NULL);

	/**
	 * @override
	 *
	 * 被db_handle.close()调用 关闭数据库conn_
	 */
	bool close(void);

	/////////////////////////////////////////////////////////////////////

	/**
	 * @override
	 *
	 * 被db_handle.dbtype()调用 返回数据库的类型描述
	 */
	const char* dbtype(void) const;

	/**
	 * @override
	 *
	 * 被db_handle.get_errno()调用 获得上次数据库操作的出错错误号
	 */
	int get_errno(void) const;

	/**
	 * @override
	 *
	 * 被db_handle.get_error()调用 获得上次数据库操作的出错错描述
	 */
	const char* get_error(void) const;

	/**
	 * @override
	 *
	 * 被db_handle.is_opened()调用 数据库是否已经打开了(即是否conn_ != NULL)
	 */
	bool is_opened(void) const;

	/////////////////////////////////////////////////////////////////////

	/**
	 * @override
	 *
	 * 被db_handle.tbl_exists()调用 执行show tables like '[tbl_name]'判断数据表是否存在
	 */
	bool tbl_exists(const char* tbl_name);

	/**
	 * @override
	 *
	 * 被db_handle.sql_select()调用 执行 SELECT SQL 语句。如果非空，则将查询结果填充进该结果对象中。否则，会引用 db_handle 内部的一个临时存储对象
	 */
	bool sql_select(const char* sql, db_rows* result = NULL);

	/**
	 * @override
	 *
	 * 被db_handle.sql_update()调用 。执行 INSERT/UPDATE/DELETE SQL 语句
	 */
	bool sql_update(const char* sql);

	/**
	 * @override
	 * 被db_handle.begin_transaction()调用，执行start transaction用来表示事务的开始，注意若要使用事务方式，
	 * 则需要在 db_mysql 的构造函数中传入的参数 auto_commit 为 false
	 */
	bool begin_transaction(void);

	/**
	 * @override
	 *
	 * 被db_handle.commit()调用，执行commit用来表示事务提交，需要在 db_mysql 的构造函数中传入的参数 auto_commit 为 false
	 */
	bool commit(void);

	/**
	 * @override
	 *
	 * 被db_handle.rollback()调用，执行rollback用来表示事务回滚，需要在 db_mysql 的构造函数中传入的参数 auto_commit 为 false
	 */
	bool rollback(void);

	/**
	 * @override
	 *
	 * 被db_handle.affect_count()调用，上次 sql 操作影响的记录行数
	 */
	int affect_count(void) const;

private:
	char*  dbaddr_;  // 数据库监听地址
	char*  dbname_;  // 数据库名
	char*  dbuser_;  // 数据库账号
	char*  dbpass_;  // 数据库账号密码
	string charset_; // 连接数据库采用的字符集

	unsigned long dbflags_;
	int    conn_timeout_;
	int    rw_timeout_;
	bool   auto_commit_;
	MYSQL* conn_;

	bool sane_mysql_query(const char* sql);
	void sane_mysql_init(const char* dbaddr, const char* dbname,
		const char* dbuser, const char* dbpass,
		unsigned long dbflags, bool auto_commit,
		int conn_timeout, int rw_timeout,
		const char* charset);
};

} // namespace acl
